import { isArray, isNil, omit, toNumber } from 'lodash-es'
import { stringify } from 'qs'
import { computed, reactive, ref, watch } from 'vue'
import { appConfig } from '../config/app';
import { useLoading } from '../hook';
import { BaseType, Data, PageIn, PageOut } from '../index';
import { currentRoute, isHashRouter } from '../router'


export const excludeParams = ['pageNo', 'pageSize', 'sorts', 'hasTotal']

const isNumeric = (str: string) => /^\d+$/.test(str)

function toType(data: any, idx: BaseType) {
  let value = data[idx]
  if (isArray(value)) {
    for (let j = 0; j < value.length; j++) {
      toType(value, j);
    }
  } else if (!isNil(value)) {
    if (value === '') {
      if (isArray(data)) {
        data.splice(idx as number, 1)
      } else {
        delete data[idx]
      }
    } else {
      if (isNumeric(value)) {
        value = Number(value)
      } else if (value === 'true') {
        value = true
      } else if (value === 'false') {
        value = false
      }
      data[idx] = value
    }
  }
}

export const processParams = (params: Data) => {
  for (const key in params) {
    toType(params, key)
  }
  return params
}

export const syncBrowser = (input: PageIn): void => {
  input.params = processParams(input.params ?? {})
  const search = stringify({ ...omit(input, 'params'), ...input.params }, { arrayFormat: 'repeat' });
  if (isHashRouter()) {
    history.replaceState({}, '', `${window.location.pathname}${window.location.hash.split('?')[0]}?${search}`)
  } else {
    history.replaceState({}, '', window.location.pathname + '?' + search)
  }
}

export function usePage<T = Data[]>(pageIn: PageIn, immediate = true, browser = false) {
  const input = reactive<PageIn>({
    hasTotal: pageIn.hasTotal,
    pageNo: pageIn.pageNo || 1,
    pageSize: pageIn.pageSize || appConfig.page.defaultPageSize,
    params: pageIn.params || {},
    sorts: pageIn.sorts,
  })

  if (browser) {
    const route = currentRoute.value
    if (route) {
      const { query } = route
      const { pageNo, pageSize, sorts } = query
      // @ts-ignore
      input.sorts = sorts || input.sorts
      input.pageNo = toNumber(pageNo || input.pageNo)
      input.pageSize = toNumber(pageSize || input.pageSize)
      input.params = Object.assign(input.params ?? {}, processParams(omit(query, excludeParams)))
    } else {
      console.warn('route: 当前路由没有初始化')
    }
  }

  browser && watch(() => input, () => syncBrowser(input), { deep: true })

  const output = ref<PageOut<Data[]>>({ total: 0, rows: [] })

  // @ts-ignore
  const [onLoad, loading] = useLoading(async (...params): Promise<void> => {
    if (pageIn.api) {
      try {
        // @ts-ignore
        output.value = await pageIn.api<T>(input, ...params)
      } catch (e) {
        output.value = { total: 0, rows: [] }
        throw e
      }
    }
  }, immediate ?? true)

  const onSort = (...args: any): void => {
    //@ts-ignore
    appConfig.page.onSort(input, ...args)
    onLoad()
  }

  const page = computed(() => ({
    current: input.pageNo,
    onChange: (pageNo: number, pageSize: number): void => {
      input.pageNo = pageNo
      input.pageSize = pageSize
      onLoad()
    },
    page: input.pageNo,
    pageNo: input.pageNo,
    pageSize: input.pageSize,
    total: output.value?.total,
  }))

  return {
    input,
    loading,
    onLoad,
    onSort,
    output,
    page,
  }
}
